package edu.gatech.app4args.utils;

import edu.gatech.app4args.SlideContentSection;

import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.DefaultListModel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.event.MenuKeyEvent;
import javax.swing.event.MenuKeyListener;

/**
 * A custom DefaultListModel used to handle specific actions such as
 * drag-and-drop and element addition. Alos uses the MenuKeyListener in order
 * to change the context menu, reflecting which nodes will be added to the list.
 *
 * @author Andrew Lunsford
 */
public class CustomListModel extends DefaultListModel implements MenuKeyListener{
	/** The context menu to display when a CompendiumNode is dropped on the list */
	private JPopupMenu contextMenu;
	/** MenuItem to select the root node */
	private JMenuItem rootCtx;
	/** MenuItem to select the children nodes */
	private JMenuItem childCtx;
	/** MenuItem to select the grandchildren nodes */
	private JMenuItem gchildCtx;
	/** Selects the root node if true */
	private boolean rootB;
	/** Selects the children nodes if true */
	private boolean childB;

	/** Reference to the SlideContentSection the list is in */
	private SlideContentSection sCS;
	/** The id of the dropped CompendiumNode */
	private String dropID;

	/**
	 * Creates a new CustomListModel. Sets all default values, creates the
	 * context menu for node drops, and adds action listeners for node selection
	 * modifier keys.
	 */
	public CustomListModel(){

		rootB = true;
		childB = true;

		contextMenu = new JPopupMenu();

		rootCtx = new JMenuItem("+ Node");
        rootCtx.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent evt){
				addNodes(rootB, false, false);
            }
        });

		childCtx = new JMenuItem("+ Children");
		childCtx.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent evt){
				addNodes(rootB, childB, false);
			}
		});

		gchildCtx = new JMenuItem("+ Grandchildren");
		gchildCtx.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent evt){
				addNodes(rootB, childB, true);
			}
		});
	}

	/**
	 * Creates a CustomListModel with a reference to the SlideContentSection
	 * the list is in. Adds listeners to rootCtx and childCtx to listen for
	 * modifier keys. I swear there isn't a LeakingThisInConstructor!!!
	 * Stupid Netbeans.
	 *
	 * @param sCS the SlideContentSection the list is in
	 */
	@SuppressWarnings("LeakingThisInConstructor")
	public CustomListModel(SlideContentSection sCS){
		this();
		this.sCS = sCS;

		rootCtx.addMenuKeyListener(this);
		childCtx.addMenuKeyListener(this);
	}

	/**
	 * Called when a node is dropped onto the list. Adds menu items accordingly
	 * if the node has children or grandchildren. Displays the context menu at
	 * the point of the drop (the mouse location).
	 *
	 * @param node the CompendiumNode that was dropped
	 * @param comp the Component to show the context menu in reference to
	 * @param point the point at which to show the context menu
	 */
	public void showContext(CompendiumNode node, Component comp, Point point){
		dropID = node.getId();

		//contextMenu.addKeyListener(this);
		contextMenu.removeAll();
		contextMenu.add(rootCtx);
		// check for children and add menu item if node has children
		if(node.hasChildren()){
			contextMenu.add(childCtx);
			// check for grandchildren and add menu item if node has grandchildren
			boolean hasGrandchildren = false;
			for(String key : node.getChildren()){
				if(sCS.getNode(key).hasChildren()){
					hasGrandchildren = true;
					break;
				}
			}
			if(hasGrandchildren){
				contextMenu.add(gchildCtx);
			}

			contextMenu.revalidate();
			contextMenu.repaint();
			contextMenu.show(comp, (int)point.getX(), (int)point.getY());
		}else{
			addNodes(true, false, false);
		}
	}

	/**
	 * Adds the selected nodes to the current slide in the section it was
	 * dropped into.
	 *
	 * @param root true if the root should be added
	 * @param children true if the children of the dropped node should be added
	 * @param gchildren true if the grandchildren of the dropped node should be added
	 */
	private void addNodes(boolean root, boolean children, boolean gchildren){
		//System.out.println("CLM> adding element");
		// add root
		if(root){
			//System.out.println("CLM> adding root");
			if(sCS.addSlideData(dropID)){
				//System.out.println("CLM> adding root node: "+dropID);
				this.addElement(sCS.getNode(dropID).getLabel());
			}
		}

		// add children
		if(children){
			//System.out.println("CLM> adding children");
			CompendiumNode temp;
			for(String keyC : sCS.getNode(dropID).getChildren()){

				temp = sCS.getNode(keyC);
				if (sCS.addSlideData(keyC)) {
					//System.out.println("CLM> adding child node: "+key);
					this.addElement(temp.getLabel());
				}
				if(gchildren){
					for(String keyG : temp.getChildren()){
						if(sCS.addSlideData(keyG)){
							//System.out.println("CLM adding grandchild node: " + keyG);
							this.addElement(sCS.getNode(keyG).getLabel());
						}
					}
				}
			}
		}

		// check child for children, add those if exist & selected
		if(gchildren){
			//System.out.println("CLM> adding grandchildren");
			for(String keyC: sCS.getNode(dropID).getChildren()){
				for(String keyG : sCS.getNode(keyC).getChildren()){
					if(sCS.addSlideData(keyG)){
						//System.out.println("CLM adding grandchild node: " + keyG);
						this.addElement(sCS.getNode(keyG).getLabel());
					}
				}
			}
		}
		// reset menu items and booleans
		rootCtx.setText("+ Root");
		childCtx.setText("+ Children");
		rootB = true;
		childB = true;
	}

	/**
	 * Actions to perform when a MenuKey is typed. Unused as of current version.
	 * 
	 * @param e
	 */
	public void menuKeyTyped(MenuKeyEvent e){
	}

	/**
	 * When holding Control or Shift, the Root or Children menu items are
	 * deselected, respectively.
	 *
	 * @param e
	 */
	public void menuKeyPressed(MenuKeyEvent e){
		switch(e.getKeyCode()){
			case KeyEvent.VK_CONTROL:
				rootCtx.setText("- Root");
				rootB = false;
				break;
			case KeyEvent.VK_SHIFT:
				childCtx.setText("- Children");
				childB = false;
				break;
		}
	}

	/**
	 * When releasing Control or Shift, the Root or Children menu items are
	 * reselected, respectively.
	 *
	 * @param e
	 */
	public void menuKeyReleased(MenuKeyEvent e){
		switch(e.getKeyCode()){
			case KeyEvent.VK_CONTROL:
				rootCtx.setText("+ Root");
				rootB = true;
				break;
			case KeyEvent.VK_SHIFT:
				childCtx.setText("+ Children");
				childB = true;
				break;
		}
	}
}